#include "util.h"
#include "log.h"
#include "terminal.c"
#define BUF_SIZ 100

int TCP_connect(const char *server_ip, uint16_t server_port)
{
    int socket_fd;
    struct sockaddr_in client_addr;
    struct sockaddr_in server_addr;

    // client_addr = set_addr("192.168.108.45", 0);
    // client_addr = set_addr("127.0.0.1", 0);
    client_addr = set_addr("172.28.11.66", 0);
    server_addr = set_addr(server_ip, server_port);

    socket_fd = get_sock(&client_addr);

    if (connect(socket_fd, (struct sockaddr *)&server_addr, IPV4_LEN) == -1)
    {
        ERR_EXIT("connect()");
    }

    return socket_fd;
}

void *ls_handler(void *args[])
{
    int sock_fd = *((int *)args[0]);
    char *path = (char *)args[1];
    char sendBuf[BUF_SIZ], recvBuf[BUFSIZ];
    /* Two Usages: ls and ls <path> */
    if (path == NULL)
    {
        if (send(sock_fd, "ls", BUF_SIZ, 0) == -1)
        {
            ERR_EXIT("sendto()");
        }
    }
    else
    {
        strncat(sendBuf, "ls ", 4);
        strncat(sendBuf, path, strlen(path));
        if (send(sock_fd, "ls", BUF_SIZ, 0) == -1)
        {
            ERR_EXIT("sendto()");
        }
    }

    if (recv(sock_fd, recvBuf, BUFSIZ, 0) == -1)
    {
        ERR_EXIT("recv()");
    }
    printf("%s\n", recvBuf);
    return NULL;
}

void *upload_handler(void *args[])
{
    int sock_fd = *((int *)args[0]);
    char *filename = (char *)args[1];
    char sendBuf[BUFSIZ], recvBuf[BUF_SIZ];
    FILE *file_fd;
    size_t bytes_read;

    file_fd = fopen(filename, "rb");
    if (file_fd == NULL)
    {
        ERR_EXIT("fopen()");
    }
    sprintf(sendBuf, "upload %s", filename);
    send(sock_fd, sendBuf, BUF_SIZE, 0);

    memset(sendBuf, 0, BUF_SIZE);
    fseek(file_fd, 0, SEEK_END);
    long fileSize = ftell(file_fd);
    fseek(file_fd, 0, SEEK_SET);

    sprintf(sendBuf, "%ld", fileSize);
    sleep(1);
    send(sock_fd, sendBuf, strlen(sendBuf) + 1, 0);
    sleep(1);
    memset(sendBuf, 0, BUFSIZ);
    recv(sock_fd, sendBuf, BUFSIZ, 0);
    memset(sendBuf, 0, BUF_SIZE);

    printf("UPLOADING...");
    fflush(stdout);
    while ((bytes_read = fread(sendBuf, 1, BUFSIZ, file_fd)) > 0)
    {
        if (send(sock_fd, sendBuf, bytes_read, 0) <= 0)
        {
            break;
        }
    }

    fclose(file_fd);
    sleep(1);

    memset(recvBuf, 0, BUF_SIZE);
    if (recv(sock_fd, recvBuf, BUF_SIZ, 0) == -1)
    {
        ERR_EXIT("recv()");
    }

    printf("\r%s\n", recvBuf);
    return NULL;
}

void *download_handler(void *args[])
{
    int sock_fd = *((int *)args[0]);
    char *path = (char *)args[1];
    char sendBuf[BUF_SIZ], recvBuf[BUF_SIZ];
    int download_size;
    FILE *download_file;
    int write_fd, cnt = 0;
    size_t bytes_read;
    ssize_t bytes_written;
    ssize_t bytes_recv;

    char cat_path[BUF_SIZE + 10];
    strcpy(cat_path, "./download/");
    strncat(cat_path, path, BUF_SIZE - strlen(cat_path) - 1);
    download_file = fopen(cat_path, "wb");
    if (download_file == NULL)
        ERR_EXIT("fopen:");

    /* obtain path of download file under the server directory */
    memset(sendBuf, 0, BUF_SIZ);
    strncat(sendBuf, "download ", 10);
    strncat(sendBuf, path, strlen(path));
    log_debug("%s", sendBuf);

    /* send download msg to server and start loading */
    if (send(sock_fd, sendBuf, BUF_SIZ, 0) == -1)
    {
        ERR_EXIT("sendto()");
    }

    /* Get msg from server and the size of file to download*/
    if (recv(sock_fd, recvBuf, BUF_SIZ, 0) == -1)
    {
        ERR_EXIT("recv()");
    }
    download_size = atoi(recvBuf);

    /* start downloading and show download progress */
    printf("Downloading...");
    fflush(stdout);
    memset(recvBuf, 0, sizeof(recvBuf));
    while ((bytes_recv = recv(sock_fd, recvBuf, sizeof(recvBuf), 0)) > 0)
    {
        if (fwrite(recvBuf, 1, bytes_recv, download_file) != bytes_recv)
        {
            perror("fwite:");
            break;
        };
        // printf("\rDownloading %.2f %%", (float)bytes_recv / (float)download_size);
        cnt += bytes_recv;
        memset(recvBuf, 0, sizeof(recvBuf));
        if (cnt >= download_size)
        {
            break;
        }
    }
    printf("\rDownload Finished\n");
    /* send FIN to server*/
    strcpy(sendBuf, "Download Finished!");
    if (send(sock_fd, sendBuf, BUF_SIZ, 0) <= 0)
    {
        ERR_EXIT("send()");
    }

    return NULL;
}

void *send_quit(void *args[])
{
    int sock_fd = *((int *)args[0]);
    char buf[BUF_SIZE];
    send(sock_fd, "quit", 5, 0);
    recv(sock_fd, buf, BUF_SIZE, 0);
    printf("%s\n", buf);
    close(sock_fd);
    exit(EXIT_SUCCESS);
}

int main(int argc, char **argv)
{
    struct terminal tml = {0};
    int sock = 0;

    // sock = TCP_connect("172.28.11.66", 8080);
    sock = TCP_connect("192.168.35.45", 8080);
    // sock = TCP_connect("127.0.0.1", 8080);

    add_option(&tml, "ls", ls_handler, &sock);
    add_option(&tml, "upload", upload_handler, &sock);
    add_option(&tml, "download", download_handler, &sock);
    add_option(&tml, "quit", send_quit, &sock);
    run_terminal(&tml, "lab_tcp");
    return 0;
}
